package com.timebank.score.service.impl;

import com.alibaba.fastjson.JSON;
import com.timebank.common.core.constant.SecurityConstants;
import com.timebank.common.core.constant.message.MessageTypeConstant;
import com.timebank.common.core.constant.rabbitmq.exchange.ExchangeConstant;
import com.timebank.common.core.constant.rabbitmq.routingkey.RoutingKeyConstant;
import com.timebank.common.core.constant.score.task.ScoreTaskConstant;
import com.timebank.common.core.exception.ServiceException;
import com.timebank.common.core.result.Result;
import com.timebank.common.core.web.pojo.AjaxResult;
import com.timebank.common.security.utils.SecurityUtils;
import com.timebank.score.constant.LotteryConstant;
import com.timebank.score.constant.PrizeTypeConstant;
import com.timebank.score.mapper.PrizeMapper;
import com.timebank.score.mapper.ScoreTaskLogMapper;
import com.timebank.score.pojo.dto.LotteryDTO;
import com.timebank.score.pojo.entity.LotteryLog;
import com.timebank.score.pojo.entity.Prize;
import com.timebank.score.pojo.entity.PrizeMessage;
import com.timebank.score.pojo.entity.ScoreTaskLog;
import com.timebank.score.service.LotteryLogService;
import com.timebank.score.service.LotteryService;
import com.timebank.score.service.ScoreTaskLogService;
import com.timebank.score.utils.LotteryUtil;
import com.timebank.system.model.LoginUser;
import com.timebank.system.pojo.SysUser;
import com.timebank.system.service.RemoteUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 抽奖 实现类
 * @author shiliuyinzhen
 */
@Service
@Slf4j
public class LotteryServiceImpl implements LotteryService {

    @Resource
    private PrizeMapper prizeMapper;

    @Resource
    private LotteryLogService lotteryLogService;

    @Resource
    private RemoteUserService remoteUserService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private ScoreTaskLogService scoreTaskLogService;

    /**
     * 开始抽奖
     * @return
     */
    @Override
    @Transactional
    public AjaxResult begin(LotteryDTO lotteryDTO) {
        //获取用户信息
        Result<LoginUser> result =
                remoteUserService.getUserInfo(SecurityUtils.getUsername(), SecurityConstants.INNER);
        if (result.getData() == null) {
            throw new ServiceException("查询用户信息失败");
        }
        LoginUser loginUser = result.getData();
        SysUser sysUser = loginUser.getSysUser();
        if (sysUser == null) {
            throw new ServiceException("查询用户信息失败");
        }
        Integer score = sysUser.getScore();
        //获取抽奖模式和抽奖积分
        Integer lotteryType = lotteryDTO.getLotteryType();
        Integer lotteryScore = lotteryDTO.getLotteryScore();
        //如果没有类型默认为单抽模式
        if (lotteryType == null){
            lotteryType = LotteryConstant.SINGLE_TYPE;
        }
        //获取奖品列表
        List<Prize> prizes = prizeMapper.getPrizeList();
        if (prizes.isEmpty() || prizes.size() <= 0) {
            throw new ServiceException("无奖品信息");
        }
        //获取初始权重
        List<Double> originRates = new ArrayList<>();
        for (Prize prize : prizes) {
            double weight = prize.getWeight();
            int stock = prize.getStock();
            // 概率为负数或者库存为0的数据，概率设置为0
            if (weight < 0 || stock == 0) {
                weight = 0;
            }
            originRates.add(weight);
        }
        //取出保底奖品：谢谢参与
        Prize prize1 = new Prize();
        prize1.setName("谢谢参与");
        Prize guarantee = prizeMapper.getPrize(prize1);
        //如果是单抽
        if (lotteryType.equals(LotteryConstant.SINGLE_TYPE)) {
            //如果积分为空，则使用默认的积分
            if (lotteryScore == null){
                lotteryScore = LotteryConstant.SINGLE_SCORE;
            }
            //如果积分不够
            if (score < lotteryScore){
                throw new ServiceException("用户积分不足");
            }
            //扣除积分
            sysUser.setScore(sysUser.getScore() - lotteryScore);
            updateUserScore(sysUser);
            int originIndex = LotteryUtil.lottery(originRates);
            Prize prize = prizes.get(originIndex);
            if (prize.getWeight() <= 0 || prize.getStock() == 0) {
                // 多一步判断
                System.out.println("库存不足或概率小于等于0======谢谢参与======");
                //返回保底奖品：谢谢参与
                return AjaxResult.success(guarantee);
            }
            //如果限量，更新库存
            updateStock(prize);
            log.info("抽中奖品：{}", prize.getName());
            //创建抽奖记录
            LotteryLog lotteryLog = LotteryLog.builder()
                    .userId(SecurityUtils.getUserId())
                    .prizeId(prize.getId())
                    .prizeName(prize.getName())
                    .drawTime(LocalDateTime.now())
                    .build();
            int row = lotteryLogService.addLotteryLog(lotteryLog);
            if (row <= 0){
                throw new ServiceException("创建抽奖记录失败");
            }
            //发放奖励
            distributePrize(prize, sysUser);
            //新增完成积分抽奖任务记录
            addScoreTaskLog();
            return AjaxResult.success(prize);
        } else if (lotteryType.equals(LotteryConstant.TEN_CONTINUOUS_TYPE)) {
            //如果积分为空，则使用默认的积分
            if (lotteryScore == null){
                lotteryScore = LotteryConstant.TEN_CONTINUOUS_SCORE;
            }
            //如果积分不够
            if (score < lotteryScore){
                throw new ServiceException("用户积分不足");
            }
            //扣除积分
            sysUser.setScore(sysUser.getScore() - lotteryScore);
            updateUserScore(sysUser);
            //如果是十连抽
            List<Prize> prizeList = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                int originIndex = LotteryUtil.lottery(originRates);
                Prize prize = prizes.get(originIndex);
                if (prize.getWeight() <= 0 || prize.getStock() == 0) {
                    // 多一步判断
                    System.out.println("库存不足或概率小于等于0======谢谢参与======");
                    prizeList.add(guarantee);
                    log.info("抽中奖品：{}", prize.getName());
                } else {
                    //更新库存
                    updateStock(prize);
                    log.info("抽中奖品：{}", prize.getName());
                    //创建抽奖记录
                    LotteryLog lotteryLog = LotteryLog.builder()
                            .userId(SecurityUtils.getUserId())
                            .prizeId(prize.getId())
                            .prizeName(prize.getName())
                            .drawTime(LocalDateTime.now())
                            .build();
                    int row = lotteryLogService.addLotteryLog(lotteryLog);
                    if (row <= 0){
                        throw new ServiceException("创建抽奖记录失败");
                    }
                    //发放奖励
                    distributePrize(prize, sysUser);
                    //将入奖品数组
                    prizeList.add(prize);
                }
            }
            //新增完成积分抽奖任务记录
            addScoreTaskLog();
            return AjaxResult.success(prizeList);
        }
        return AjaxResult.error("未知抽奖类型");
    }

    /**
     * 新增完成积分抽奖任务记录
     */
    public void addScoreTaskLog() {
        //新增完成积分抽奖任务记录
        ScoreTaskLog scoreTaskLog = ScoreTaskLog.builder()
                .taskId(ScoreTaskConstant.SCORE_LOTTERY)
                .userId(SecurityUtils.getUserId())
                .finishTime(LocalDateTime.now())
                .remark("完成积分抽奖")
                .build();
        AjaxResult ajaxResult = scoreTaskLogService.addScoreTaskLog(scoreTaskLog);
        if (ajaxResult.isError()){
            throw new ServiceException("记录完成积分任务失败");
        }
    }

    /**
     * 更新库存
     * @param prize
     */
    private void updateStock(Prize prize) {
        //如果限量，更新库存
        if (!(prize.getStock() == -1 && prize.getTotal() == -1)) {
            prize.setStock(prize.getStock() - 1);
            int row  = prizeMapper.updatePrize(prize);
            if (row <= 0){
                throw new ServiceException("更新库存失败");
            }
        }
    }

    /**
     * 发放奖品
     *
     * @param prize
     * @param sysUser
     */
    public Boolean distributePrize(Prize prize, SysUser sysUser){
        Integer typeId = prize.getTypeId();
        //如果是时间币奖励
        if (typeId.equals(PrizeTypeConstant.TIME_COIN_PRIZE)){
            //发送消息，异步处理
            PrizeMessage msg = PrizeMessage.builder()
                    .prizeId(prize.getId())
                    .senderId(999L)
                    .senderName("system")
                    .getterId(SecurityUtils.getUserId())
                    .title("积分抽奖获得奖励")
                    .content("积分抽奖获得时间币："+prize.getTimeCoin())
                    .typeId(MessageTypeConstant.SCORE_PRIZE_GET_TIMECOIN)
                    .build();
            //将消息转换为json格式
            String msgJSON = JSON.toJSONString(msg);
            //向用户，发出奖品到账通知
            rabbitTemplate.convertAndSend(ExchangeConstant.PRIZE_MESSAGE_EXCHANGE, RoutingKeyConstant.PRIZE_MESSAGE_ROUTING_KEY,msgJSON);
        } else if (typeId.equals(PrizeTypeConstant.SCORE_PRIZE)) {
            //如果是积分奖品
            sysUser.setScore(sysUser.getScore() + prize.getScore());
            Result<Boolean> result =
                    remoteUserService.updateUserLoginInfo(sysUser, SecurityConstants.INNER);
            if (result.getData() == null || result.getData() == false){
                throw new ServiceException("更新用户积分失败");
            }
        } else if (typeId.equals(PrizeTypeConstant.PARTICIPATION_PRIZE)){
            //如果是参与奖
            return true;
        }else {
            throw new ServiceException("未知奖品类型,发放奖品失败");
        }
        return false;
    }

    /**
     * 扣除用户积分
     * @param sysUser
     */
    public void updateUserScore(SysUser sysUser) {
        Result<Boolean> res =
                remoteUserService.updateUserLoginInfo(sysUser, SecurityConstants.INNER);
        if (res.getData() == null || res.getData() == false){
            throw new ServiceException("扣除用户积分失败");
        }
    }

}
